/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package db;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.logging.*;
import models.Person;
import models.roles.*;

/**
 *
 * @author pelle
 */
public class PersonDb extends AbstractDb implements PersonInterface {

    // Få antallet af medlemmer ud
    public int getMemberCount() {
        int count = 0;
        ResultSet rs = this.db.query("SELECT COUNT(*) AS count FROM Lif_Person JOIN Lif_Roles ON person_id = roles_personid WHERE roles_isMember = 1");

        try {
            if (rs != null && rs.next()) {
                // Henter count fra den returnerede tabel
                count = rs.getInt("count");
            }
        } catch (SQLException ex) {
            Logger.getLogger(PersonDb.class.getName()).log(Level.SEVERE, null, ex);
        }

        return count;
    }

    // Få antallet af trænere ud
    public int getTrainerCount() {
        int count = 0;
        ResultSet rs = this.db.query("SELECT COUNT(*) AS count FROM Lif_Person JOIN Lif_Roles ON person_id = roles_personid WHERE roles_isTrainer = 1");

        try {
            if (rs != null && rs.next()) {
                // Henter count fra den returnerede tabel
                count = rs.getInt("count");
            }
        } catch (SQLException ex) {
            Logger.getLogger(PersonDb.class.getName()).log(Level.SEVERE, null, ex);
        }

        return count;
    }

    // Metode til at 
    public Person getPersonById(int id) {
        return this.getPersonById(id, true);
    }

    // Henter overloaded metode til at hente en person ud fra ID
    // "boolean getExtra" vælger om den selv skal sætte fx bynavn i personen, hvis det ikke er med i SQL forspørgslen
    public Person getPersonById(int id, boolean getExtra) {
        Person person = null;
        if (id > 0) {
            ResultSet result = this.db.query(""
                    + "SELECT * "
                    + "FROM Lif_Person "
                    + "JOIN Cities ON cities_zipcode = person_zipcode "
                    + "WHERE person_id = " + id);

            try {
                if (result != null && result.next()) {
                    // Kører metoden til at bygge en person ud fra databasesvaret
                    // Sender "getExtra" med, kan være nogen ikke vil have roller med
                    person = this.buildPerson(result, getExtra);
                }
            } catch (SQLException ex) {
                Logger.getLogger(LoginDb.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return person;
    }

    // Find en person ud fra personens e-mail adresse
    public Person getPersonByEmail(String email) {
        Person person = null;
        if (email.length() > 0) {
            ResultSet result = this.db.query(""
                    + "SELECT * "
                    + "FROM Lif_Person "
                    + "JOIN Cities ON cities_zipcode = person_zipcode "
                    + "WHERE person_email = '" + email + "'");
            try {
                if (result != null && result.next()) {
                    // Kører metoden til at bygge en person ud fra databasesvaret
                    person = this.buildPerson(result);
                }
            } catch (SQLException ex) {
                Logger.getLogger(LoginDb.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return person;
    }

    // Metode til at finde personer ud fra nogle søgekriterier
    public ArrayList<Person> searchPersons(String name, String email, String phone, String cpr, ArrayList<String> roles) {
        ArrayList<Person> persons = new ArrayList<Person>();
        
        // Tjekker om der overhovedet er nogle data som vi kan søge med
        if (name.length() > 0 || email.length() > 0 || phone.length() > 0 || cpr.length() > 0 || (roles != null && roles.size() > 0)) {

            String queryWhere = "";
            
            // Er der et navn vi skal søge efter?
            if(name.length() > 0) {
                // Tilføjer vores where-statement
                queryWhere += "person_name LIKE '" + name + "%'";
            }
            
            // Er der en email vi skal søge efter?
            if(email.length() > 0) {
                // Tilføjer et " AND " hvis der er nogle andre søgekriterier i strengen i forvejen
                // Og derefter vores SQL-tilføjelse
                queryWhere += (queryWhere.length() > 0 ? " AND " : "") 
                        + "person_email = '" + email + "'";
            }
            
            // Er der et telefonnummer vi skal søge efter?
            if(phone.length() > 0) {
                // Tilføjer et " AND " hvis der er nogle andre søgekriterier i strengen i forvejen
                // Og derefter vores SQL-tilføjelse
                queryWhere += (queryWhere.length() > 0 ? " AND " : "") 
                        + "person_phone = '" + phone + "'";
            }
            
            // Er der et CPR-nummer vi skal søge efter?
            if(cpr.length() > 0) {
                // Tilføjer et " AND " hvis der er nogle andre søgekriterier i strengen i forvejen
                // Og derefter vores SQL-tilføjelse
                queryWhere += (queryWhere.length() > 0 ? " AND " : "") 
                        + "person_cpr LIKE '" + cpr + "%'";
            }
            
            // Er der nogle roller vi skal søge efter?
            if(roles != null) {
                
                // Løber alle rollerne igennem
                for(String s : roles) {
                    String r = "";

                    // Er det et medlem vi skal finde?
                    if(s.equals("member")) {
                        r = "roles_isMember";
                        
                    // Er det ledelsen vi skal finde?
                    } else if(s.equals("manager")) {
                        r = "roles_isManagement";
                     
                    // Er det en træner vi skal finde?
                    } else if(s.equals("trainer")) {
                        r = "roles_isTrainer";
                        
                    // Er det en inspektør vi skal finde?
                    } else if(s.equals("inspector")) {
                        r = "roles_isInspector";
                        
                    // Er det en sponsor vi skal finde?
                    } else if(s.equals("sponsor")) {
                        // Tilføjer et " AND " hvis der er nogle andre søgekriterier i strengen i forvejen
                        // Og derefter vores SQL-tilføjelse
                        queryWhere += (queryWhere.length() > 0 ? " AND " : "") 
                                + "roleSponsor_personid IS NOT NULL";
                    }

                    // Er der nogle roller som skal tilføjes til vores "where-statement"?
                    if(r.length() > 0) {
                        
                        // Tilføjer et " AND " hvis der er nogle andre søgekriterier i strengen i forvejen
                        // Derefter tilføjer vi et " = 1", så det fx kommer til at være "roles_isTrainer = 1"
                        queryWhere += (queryWhere.length() > 0 ? " AND " : "") 
                                + r + " = 1";
                    }
                }
            }

            // Den færdige SQL, med where-tilføjelse på
            ResultSet rs = this.db.query(""
                    + "SELECT * "
                    + "FROM Lif_Person "
                    + "JOIN Cities ON cities_zipcode = person_zipcode "
                    + "JOIN Lif_Roles ON roles_personid = person_id "
                    + "LEFT JOIN Lif_RoleSponsor ON roleSponsor_personid = person_id "
                    + "WHERE " + queryWhere);

            try {
                if(rs != null) {
                    // Løber alle personerne igennem fra database-svaret
                    while(rs.next()) {
                        
                        // Bygger personen
                        Person person = this.buildPerson(rs);
                        if (person != null) {
                            // Tilføjer personen til vores arrayliste som skal sendes tilbage
                            persons.add(person);
                        }
                    }
                }
            } catch (SQLException ex) {
                Logger.getLogger(LoginDb.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return persons;
    }

    public Person buildPerson(ResultSet rs) {
        return buildPerson(rs, true);
    }

    // Overloaded methode til at bygge et person-objekt ud fra resultatet fra databasen
    // "boolean getExtra" vælger om den selv skal sætte fx bynavn i personen, hvis det ikke er med i SQL forspørgslen
    public Person buildPerson(ResultSet rs, boolean getExtra) {
        Person person = null;
        
        // Tjekker om der overhovedet er noget
        if(rs != null) {
            try {
                
                // Finder om der er et personid overhovedet
                int userId = rs.getInt("person_id");
                if (userId > 0) {

                    // Bygger den nye person
                    person = new Person(
                            rs.getInt("person_id"),
                            rs.getString("person_name"),
                            rs.getString("person_address"),
                            rs.getInt("person_zipcode"),
                            rs.getString("person_email"),
                            rs.getString("person_phone"),
                            rs.getString("person_cpr"));

                    String city = "";
                    // Prøver om der er et bynavn i databaseresultatet
                    try {
                        city = rs.getString("cities_name");
                    } catch (SQLException ex) {
                        // Ellers beder vi om det, ud fra brugerens ID
                        city = this.getCityById(userId);
                    }
                    // Sætter bynavnet til person-objektet
                    person.setCity(city);


                    ArrayList<AbstractRole> roles = null;
                    // Prøver om der er nogle brugerroller med i forespørgslen
                    try {
                        // Kører metoden "buildRoles".
                        // Sender en exception hvis der ikke er noget med i databaseresultatet
                        roles = this.buildRoles(rs);
                    } catch (SQLException ex) {
                        // Tjekker om vi overhovedet behøver finde det
                        if(getExtra) {
                            // Finder rollerne til personen ud fra brugerens ID
                            roles = this.getRolesById(userId);
                        }
                    }
                    // Sætter rollerne til person-objektet (eller null, hvis der ikke er nogen)
                    person.setPersonRoles(roles);

                    ArrayList<Person> contacts = null;
                    // Skal vi hente relaterede personer ud til en person?
                    if(getExtra) {
                        // Det skal vi. Henter relaterede personer, ud fra brugeren ID
                        contacts = this.getContactsById(userId);
                    }
                    // Sætter de relaterede personer
                    person.setContacts(contacts);

                }
            } catch (SQLException ex) {
                Logger.getLogger(LoginDb.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return person;
    }

    // Metode til at finde bynavnet som en person er fra
    public String getCityById(int id) {
        String city = "";
        // Har vi noget ID overhovedet?
        if(id > 0) {
            try {
                ResultSet rs = this.db.query("SELECT TOP 1 cities_name "
                        + "FROM Cities "
                        + "JOIN Lif_Person ON cities_zipcode = person_zipcode "
                        + "WHERE person_id = " + id);
                if (rs != null && rs.next()) {
                    // Finder bynavnet i resultatet
                    city = rs.getString("cities_name");
                }
            } catch (SQLException ex) {
                Logger.getLogger(LoginDb.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return city;
    }

    // Metode til at finde roller til en person, ud fra ID
    public ArrayList<AbstractRole> getRolesById(int id) {
        ArrayList<AbstractRole> roles = new ArrayList<AbstractRole>();
        if (id > 0) {
            try {
                ResultSet rs = this.db.query(""
                        + "SELECT TOP 1 roles_personid, roles_isMember, roles_isTrainer, roles_isManagement, roles_isInspector, roleSponsor_personid, roleSponsor_companyName, roleSponsor_website "
                        + "FROM Lif_Roles "
                        + "JOIN Lif_Person ON roles_personid = person_id "
                        + "LEFT JOIN Lif_RoleSponsor ON roleSponsor_personid = person_id "
                        + "WHERE person_id = " + id);

                if (rs != null && rs.next()) {
                    // Bygger rollerne, og får et array tilbage med rollerne i
                    roles = this.buildRoles(rs);
                }
            } catch (SQLException ex) {
                Logger.getLogger(LoginDb.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return roles;
    }

    // Metode til at finde relaterede personer ud fra et bruger ID
    public ArrayList<Person> getContactsById(int id) {
        ArrayList<Person> contacts = new ArrayList<Person>();
        if (id > 0) {
            try {
                ResultSet rs = this.db.query(""
                        + "SELECT * "
                        + "FROM Lif_PersonRelation "
                        + "JOIN Lif_Person ON personRelation_relatedPersonid = person_id "
                        + "JOIN Cities ON cities_zipcode = person_zipcode "
                        + "WHERE personRelation_personid = " + id);

                if (rs != null) {
                    // Bygger kontakterne og får returneret et arraylist
                    contacts = this.buildContacts(rs);
                }
            } catch (SQLException ex) {
                Logger.getLogger(LoginDb.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return contacts;
    }

    // Metode til at bygge rollerne ud fra et database svar
    // Smider en exception hvis noget ikke findes i databasesvaret
    public ArrayList<AbstractRole> buildRoles(ResultSet rs) throws SQLException {
        ArrayList<AbstractRole> roles = new ArrayList<AbstractRole>();

        if (rs != null) {
            int personId = rs.getInt("roles_personid");
            int isMember = rs.getInt("roles_isMember");
            int isTrainer = rs.getInt("roles_isTrainer");
            int isManagement = rs.getInt("roles_isManagement");
            int isInspector = rs.getInt("roles_isInspector");
            int isSponsor = rs.getInt("roleSponsor_personid");
            String sponsorName = rs.getString("roleSponsor_companyName");
            String sponsorWebsite = rs.getString("roleSponsor_website");
            
            // Tilføjer medlemskab hvis det er sat
            if(isMember > 0) {
                Member member = new Member();
                roles.add(member);
            }

            // Tilføjer træner hvis det er sat
            if(isTrainer > 0) {
                Trainer trainer = new Trainer();
                roles.add(trainer);
            }

            // Tilføjer ledelse hvis det er sat
            if(isManagement > 0) {
                Manager manager = new Manager();
                roles.add(manager);
            }

            // Tilføjer inspektør hvis det er sat
            if(isInspector > 0) {
                Inspector inspector = new Inspector();
                roles.add(inspector);
            }

            // Tilføjer sponsor hvis det er sat
            if(isSponsor > 0) {
                Sponsor sponsor = new Sponsor(sponsorName, sponsorWebsite);
                roles.add(sponsor);
            }

        }

        return roles;
    }

    // Bygger rollearray. Smider SQLException ved fejl
    public ArrayList<Person> buildContacts(ResultSet rs) throws SQLException {
        ArrayList<Person> contacts = new ArrayList<Person>();

        if(rs != null) {
            // Looper alle kontakterne igennem
            while(rs.next()) {
                // Bygger personen med den nuværende database-række
                // Sætter til "false" fordi vi ikke vil have fx roller med til personen
                contacts.add(this.buildPerson(rs, false));
            }
        } else {
            throw new SQLException();
        }

        return contacts;
    }

    // Metode til at finde bynavnet ud fra postnummer
    public String getCityFromZipcode(int zipcode) {
        String city = "";
        if (zipcode > 0) {
            ResultSet rs = this.db.query("SELECT cities_name FROM Cities WHERE cities_zipcode = " + zipcode);
            try {
                if (rs != null && rs.next()) {
                    city = rs.getString("cities_name");
                }
            } catch (SQLException ex) {
                Logger.getLogger(PersonDb.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return city;
    }

    // Public metode til at gemme en person
    public int savePerson(Person person) {
        int result = 0;
        
        if(person != null) {
            
            // Skal vi opdatere en person, eller gemme en ny?
            // Er der et brugerid, så må vi skulle opdatere
            if(person.getId() > 0) {
                result = this.updatePerson(person);
            } else {
                // Er der ikke, så opretter vi en ny person
                result = this.saveNewPerson(person);
            }
        }

        return result;
    }

    // Metode til at gemme en ny person i databasen
    private int saveNewPerson(Person person) {
        boolean update = false;
        int personId = 0;
        if (person != null) {

            String queryPerson = "INSERT INTO Lif_Person ("
                    + "person_name, person_address, person_zipcode, person_email, person_password, person_cpr, person_phone"
                    + ") VALUES ("
                    + "'" + person.getName() + "', "
                    + "'" + person.getAddress() + "', "
                    + "" + person.getZipcode() + ", "
                    + "'" + person.getEmail() + "', "
                    + "'" + person.getPassword() + "', "
                    + "'" + person.getCpr() + "', "
                    + "'" + person.getPhone() + "' "
                    + ");";

            // Gemmer personen i databasen og får det nye brugerID tilbage
            int p = this.db.queryInsert(queryPerson);

            // Fik vi et brugerid?
            if(p > 0) {
                // Tilføjer ID'et til person-objektet
                person.setId(p);

                // "Simple roller" bygge i en String
                String queryRoles = "INSERT INTO Lif_Roles ("
                        + "roles_personid, roles_isMember, roles_isTrainer, roles_isManagement, roles_isInspector"
                        + ") VALUES ("
                        + p + ", "
                        + (person.isMember() ? 1 : 0) + ", "
                        + (person.isTrainer() ? 1 : 0) + ", "
                        + (person.isManager() ? 1 : 0) + ", "
                        + (person.isInspector() ? 1 : 0) + ""
                        + ");";

                // Bygger en SQL sætning til os, for at gemme hvis personen er sponsor
                String queryRoleSponsor = this.buildSaveRoleSponsor(person);

                // Bygger en SQL sætning til os, for at gemme relaterede personer
                String queryRelation = this.buildSavePersonRelations(person);

                // Laver ny transaktion
                this.newTransaction();
                // Tilføjer vores SQL statements til transaktionen
                this.addStatement(queryRoles);
                this.addStatement(queryRoleSponsor);
                this.addStatement(queryRelation);

                // Tjekker om der er 3 som der skal være
                if(this.getStatementCount() == 3) {
                    // Committer transaktionen, eller udfører rollback ved fejl
                    if (this.commitTransaction()) {
                        // Tilføjer person id'et til vores variabel der skal returneres
                        personId = p;
                    }
                }
            }
        }
        return personId;
    }

    private int updatePerson(Person person) {
        int success = 0;
        boolean update = true;

        if (person != null) {

            // Person oplysninger SQL string
            String queryPerson = "UPDATE Lif_Person "
                    + "SET "
                    + "person_name = '" + person.getName() + "', "
                    + "person_address = '" + person.getAddress() + "', "
                    + "person_zipcode = " + person.getZipcode() + ", "
                    + "person_email = '" + person.getEmail() + "', "
                    + (person.getPassword().length() > 0 ? ("person_password = '" + person.getPassword() + "', ") : "")
                    + "person_cpr = '" + person.getCpr() + "', "
                    + "person_phone = '" + person.getPhone() + "' "
                    + "WHERE person_id = " + person.getId();

            // "Simple roller" SQL string
            String queryRoles = "UPDATE Lif_Roles "
                    + "SET "
                    + "roles_isMember = " + (person.isMember() ? 1 : 0) + ", "
                    + "roles_isTrainer = " + (person.isTrainer() ? 1 : 0) + ", "
                    + "roles_isManagement = " + (person.isManager() ? 1 : 0) + ", "
                    + "roles_isInspector = " + (person.isInspector() ? 1 : 0) + " "
                    + "WHERE roles_personid = " + person.getId();

            // Sponsor roller (tømmes før de sættes ind igen)
            String queryRoleSponsorDelete = "DELETE FROM Lif_RoleSponsor WHERE roleSponsor_personid = " + person.getId() + ";";
            // Bygger SQLsætning til at gemme sponsoroplysninger
            String queryRoleSponsor = this.buildSaveRoleSponsor(person);

            // Sletter alle personrelationer
            String queryRelationDelete = "DELETE FROM Lif_PersonRelation WHERE personRelation_personid = " + person.getId() + ";";
            // Bygger SQLsætning til at gemme personrelationer
            String queryRelation = this.buildSavePersonRelations(person);

            // Laver ny transaktion
            this.newTransaction();
            // Tilføjer statements til transaktionen
            this.addStatement(queryPerson);
            this.addStatement(queryRoles);
            this.addStatement(queryRoleSponsorDelete);
            this.addStatement(queryRoleSponsor);
            this.addStatement(queryRelationDelete);
            this.addStatement(queryRelation);
            // Sikre os at der er 3 statements, så noget ikke er gået galt
            if (this.getStatementCount() == 6) {
                // Committer transaktionen eller udfører rollback ved fejl
                if (this.commitTransaction()) {
                    success = 1;
                }
            }
        }

        return success;
    }

    /*
     * Byggemetoder til at gemme/opdatere personer
     */
    // Metode til at bygge en SQL sætning for at gemme sponsoroplysninger
    private String buildSaveRoleSponsor(Person person) {
        String query = "";
        
        // Tjekker først om der er en person, og om personen overhovedet er en sponsor
        if(person != null && person.isSponsor()) {
            Sponsor s = null;
            // Løber er rollerne igennem for at finde sponsor-objektet
            for(AbstractRole a : person.getPersonRoles()) {
                // Er du et sponsor objekt?
                if(a instanceof Sponsor) {
                    // Typecaster objektet til "Sponsor" så vi kan kigge i det
                    s = (Sponsor)a;
                }
            }
            
            // Hvis vi fandt sponsor objektet, så kan vi fortsætte
            if(s != null) {
                // Bygger SQL statementet
                query += "INSERT INTO Lif_RoleSponsor ("
                        + "roleSponsor_personid, roleSponsor_companyName, roleSponsor_website"
                        + ") VALUES ("
                        + person.getId() + ", "
                        + "'" + s.getCompanyName() + "', "
                        + "'" + s.getWebsite() + "'"
                        + ");";
            }
        }

        return query;
    }

    // Metode til at bygge en SQL sætning for at gemme relaterede personer
    private String buildSavePersonRelations(Person person) {
        String query = "";
        String queryInsert = "";
        
        // Har vi en person, og har han overhovedet nogle relaterede personer i sig som vi skal gemme?
        if(person != null && person.getContacts().size() > 0) {
            // Bygger starten af SQL statementet "INSERT INTO blah (felt1, felt 1) VALUES"
            query += "INSERT INTO Lif_PersonRelation ("
                    + "personRelation_personid, personRelation_relatedPersonid"
                    + ") VALUES ";
            
            // Looper relaterede personer igennem
            for(Person p : person.getContacts()) {
                // Hvis det ikke er det første vi har tilføjet, så skal der lige et komma ind, for at adskille personværdierne
                if(queryInsert.length() > 0) {
                    queryInsert += ", ";
                }
                
                // Tilføjer værdierne så de passer til ovenstående INSERT-statement
                // Fx (1, 3)
                queryInsert += "(" + person.getId() + ", " + p.getId() + ")";
            }

            query += queryInsert + ";";
        }

        return query;
    }

    public ArrayList<Person> getAllPersons() {
        ArrayList<Person> personList = new ArrayList<Person>();
        ResultSet rs = this.db.query("SELECT * FROM Lif_Person");
        try {
            while (rs.next()) {
                Person p = new Person(
                        rs.getInt("person_id"),
                        rs.getString("person_name"),
                        rs.getString("person_address"),
                        rs.getInt("person_zipcode"),
                        rs.getString("person_email"),
                        rs.getString("person_phone"),
                        rs.getString("person_cpr"));
                personList.add(p);
            }

        } catch (SQLException ex) {
            Logger.getLogger(PersonDb.class.getName()).log(Level.SEVERE, null, ex);
        }
        return personList;
    }
}
